﻿//Address Geocoding with c#
//Source: http://jasonjano.wordpress.com/2010/02/10/address-geocoding/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Xml.Linq;
using System.Net;
using System.Web;
using System.Configuration;
using System.IO;
using System.Xml;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;

namespace LetLord.Models
{
    public enum LatLngAccurateToTypes : int
    {
        Unknown = 0, Country = 1, Region = 2, SubRegion = 3, Town = 4,
        PostCode = 5, Street = 6, Intersection = 7, Address = 8, Premises = 9
    }

    [Table("Address")]
    public class Address
    {
        [Key]
        [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
        public int AddressId { get; set; }
        public string Latitude { get; set; }
        public string Longitude { get; set; }
        public string Line1 { get; set; }
        public string Line2 { get; set; }
        public string TownCity { get; set; }
        public string County { get; set; }
        public string PostCode { get; set; }

        // An Address belongs to 1 ResidentialProperty
        //public int ResidentialPropertyId { get; set; }
        //[ForeignKey("ResidentialPropertyId")]
        //public virtual ResidentialProperty ResidentialProperty { get; set; }

        private LatLngAccurateToTypes _LatLngAccuracy = 0;

        public LatLngAccurateToTypes LatLngAccuracy
        {
            get { return _LatLngAccuracy; }
        }

        public override string ToString()
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            sb.AppendLine(Line1);
            if (!string.IsNullOrEmpty(Line2)) sb.AppendLine(Line2);
            sb.Append(TownCity);
            sb.Append(", ");
            sb.Append(County);
            sb.Append(" ");
            sb.Append(PostCode);
            return sb.ToString();
        }

        public Address()
        {
        }

        public void GeoCode()
        {
            //get the maps key in the web config file
            string mapskey = ConfigurationManager.AppSettings["GoogleMapsAPIKey"];
            //setup a streamreader for retrieving data from Google.
            StreamReader sr = null;

            //Check to see if our maps key exists
            if (string.IsNullOrEmpty(mapskey))
            {
                throw new Exception("No valid google maps api key to use for geocoding.  Please add an app key named \"GoogleMapsAPIKey\" to the web.config file.");
            }

            //Create the url string to send our request to googs.
            string url = string.Format("http://maps.google.com/maps/geo?q={0} +{1} +{2} +{3} +{4}&output=xml&oe=utf8&sensor=false&key={5}", this.Line1, this.Line2, this.TownCity + ", ", this.County, this.PostCode, mapskey);

            //Create a web request client.
            WebClient wc = new WebClient();

            try
            {
                //retrieve our result and put it in a streamreader
                sr = new StreamReader(wc.OpenRead(url));
            }
            catch (Exception ex)
            {
                throw new Exception("An error occured while retrieving GeoCoded results from Google, the error was: " + ex.Message);
            }

            try
            {
                //now lets parse the returned data as an xml
                XmlTextReader xtr = new XmlTextReader(sr);
                bool coordread = false;
                bool accread = false;
                while (xtr.Read())
                {
                    xtr.MoveToElement();
                    switch (xtr.Name)
                    {
                        case "AddressDetails": //Check for the address details node
                            while (xtr.MoveToNextAttribute())
                            {
                                switch (xtr.Name)
                                {
                                    case "Accuracy": //move into the accuracy attrib and....
                                        if (!accread)
                                        {
                                            //get the accuracy, convert it to our enum and save it to the record
                                            this._LatLngAccuracy = (LatLngAccurateToTypes)Convert.ToInt32(xtr.Value);
                                            accread = true;
                                        }
                                        break;
                                }
                            }
                            break;
                        case "coordinates": //the coordinates element
                            if (!coordread)
                            {
                                //move into the element value
                                xtr.Read();

                                //split the coords and then..
                                string[] coords = xtr.Value.Split(new char[] { ',' });

                                //save the values
                                Longitude = coords[0];
                                Latitude = coords[1];

                                //finally, once this has been done, we don't want the process to run again on the same file
                                coordread = true;
                            }
                            break;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception("An error occured while parsing GeoCoded results from Google, the error was: " + ex.Message);
            }
        }
    }
}